﻿using System.Globalization;
using System.Runtime.InteropServices;

namespace System.NETool;

public static class ArrayEx
{
    /// <summary>
    /// 获取数组首地址
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <returns>数组首地址</returns>
    public static IntPtr Handle(this byte[] @this)
    {
        return Marshal.UnsafeAddrOfPinnedArrayElement(@this, 0);
    }

    /// <summary>
    /// 乱序排列一个数组
    /// </summary>
    /// <typeparam name="T">数组元素类型</typeparam>
    /// <param name="this">数组</param>
    public static void Disorder<T>(this T[] @this)
    {
        if (@this == null || @this.Length == 0) return;
        var random = Random.Shared;
        for (int i = @this.Length - 1; i > 0; i--)
        {
            int j = random.Next(0, i + 1);
            (@this[i], @this[j]) = (@this[j], @this[i]);
        }
    }

    /// <summary>
    /// 取中值
    /// </summary>
    /// <typeparam name="T">数组元素类型</typeparam>
    /// <param name="this">数组</param>
    /// <returns>中值</returns>
    /// <exception cref="ArgumentNullException">数组为空时抛出</exception>
    public static T MedianNum<T>(this T[] @this) where T : IComparable
    {
        if (@this == null || @this.Length == 0)
        {
            throw new ArgumentNullException(nameof(@this));
        }

        var sortedArray = @this.OrderBy(x => x).ToArray();
        return sortedArray[sortedArray.Length / 2];
    }

    /// <summary>
    /// 字节数组转十六进制字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <param name="span">分隔符</param>
    /// <returns>十六进制字符串</returns>
    public static string ToHexString(this byte[] @this, string span = "")
    {
        if (@this == null || @this.Length == 0)
            return string.Empty;

        return ReadOnlySpanEx.ToHexString(@this, span);
    }

    /// <summary>
    /// 十六进制字符串转字节数组
    /// </summary>
    /// <param name="this">十六进制字符串</param>
    /// <param name="span">分隔符</param>
    /// <returns>字节数组</returns>
    /// <exception cref="ArgumentException">字符串长度为奇数或转换失败时抛出</exception>
    public static byte[] ToHexByteArray(this string @this, string span = "")
    {
        if (!string.IsNullOrEmpty(span))
        {
            @this = @this.Replace(span, "");
        }

        if (string.IsNullOrEmpty(@this))
        {
            return [];
        }

        if (@this.Length % 2 != 0)
        {
            throw new ArgumentException("The length of the cipherText must be an even number.", nameof(@this));
        }

        var array = new byte[@this.Length / 2];
        try
        {
            var spanStr = @this.AsSpan();
            for (int i = 0; i < array.Length; i++)
            {
                array[i] = byte.Parse(spanStr.Slice(2 * i, 2), NumberStyles.AllowHexSpecifier);
            }
        }
        catch (Exception ex)
        {
            throw new ArgumentException("Data conversion to hexadecimal failed.", nameof(@this), ex);
        }

        return array;
    }

    /// <summary>
    /// 打印字节数组为十六进制字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <param name="title">标题</param>
    /// <param name="color">颜色</param>
    public static void HexPrint(this byte[] @this, string title = "", ConsoleColor color = ConsoleColor.Blue)
    {
        if (@this == null || @this.Length == 0) return;
        ReadOnlySpanEx.HexPrint(@this.AsSpan(), title, color);
    }

    /// <summary>
    /// 打印字节数组为十六进制字符串（带偏移量和计数）
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <param name="offset">偏移量</param>
    /// <param name="count">计数</param>
    /// <param name="title">标题</param>
    /// <param name="color">颜色</param>
    /// <exception cref="ArgumentOutOfRangeException">偏移量或计数无效时抛出</exception>
    public static void HexPrint(this byte[] @this, int offset, int count, string title = "", ConsoleColor color = ConsoleColor.Blue)
    {
        if (@this == null || @this.Length == 0) return;
        if (offset < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(offset), "Offset must be greater than or equal to zero.");
        }

        if (offset + count > @this.Length)
        {
            throw new ArgumentOutOfRangeException(nameof(count), "Count must be less than write position.");
        }

        ReadOnlySpanEx.HexPrint(@this.AsSpan().Slice(offset, count), title, color);
    }

    /// <summary>
    /// 将结构体数组转换为字节数组
    /// </summary>
    /// <typeparam name="T">结构体类型</typeparam>
    /// <param name="this">结构体数组</param>
    /// <returns>字节数组</returns>
    public static Span<byte> AsBytes<T>(this T[] @this) where T : struct
    {
        return MemoryMarshal.AsBytes(@this.AsSpan());
    }

    /// <summary>
    /// 将一种结构体数组转换为另一种结构体数组
    /// </summary>
    /// <typeparam name="TFrom">源结构体类型</typeparam>
    /// <typeparam name="TTo">目标结构体类型</typeparam>
    /// <param name="this">源结构体数组</param>
    /// <returns>目标结构体数组</returns>
    public static Span<TTo> Cast<TFrom, TTo>(this TFrom[] @this) where TFrom : struct where TTo : struct
    {
        return MemoryMarshal.Cast<TFrom, TTo>(@this);
    }

    /// <summary>
    /// 计算字节数组的MD5哈希值并返回十六进制字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <returns>MD5哈希值的十六进制字符串</returns>
    public static string MD5(this byte[] @this) => ReadOnlySpanEx.MD5(@this);

    /// <summary>
    /// 计算字节数组的SHA1哈希值并返回十六进制字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <returns>SHA1哈希值的十六进制字符串</returns>
    public static string SHA1(this byte[] @this) => ReadOnlySpanEx.SHA1(@this);

    /// <summary>
    /// 计算字节数组的SHA256哈希值并返回十六进制字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <returns>SHA256哈希值的十六进制字符串</returns>
    public static string SHA256(this byte[] @this) => ReadOnlySpanEx.SHA256(@this);

    /// <summary>
    /// 计算字节数组的SHA384哈希值并返回十六进制字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <returns>SHA384哈希值的十六进制字符串</returns>
    public static string SHA384(this byte[] @this) => ReadOnlySpanEx.SHA384(@this);

    /// <summary>
    /// 计算字节数组的SHA512哈希值并返回十六进制字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <returns>SHA512哈希值的十六进制字符串</returns>
    public static string SHA512(this byte[] @this) => ReadOnlySpanEx.SHA512(@this);

    /// <summary>
    /// 计算字节数组的SHA3_256哈希值并返回十六进制字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <returns>SHA3_256哈希值的十六进制字符串</returns>
    public static string SHA3_256(this byte[] @this) => ReadOnlySpanEx.SHA3_256(@this);

    /// <summary>
    /// 计算字节数组的SHA3_384哈希值并返回十六进制字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <returns>SHA3_384哈希值的十六进制字符串</returns>
    public static string SHA3_384(this byte[] @this) => ReadOnlySpanEx.SHA3_384(@this);

    /// <summary>
    /// 计算字节数组的SHA3_512哈希值并返回十六进制字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <returns>SHA3_512哈希值的十六进制字符串</returns>
    public static string SHA3_512(this byte[] @this) => ReadOnlySpanEx.SHA3_512(@this);

    /// <summary>
    /// 生成PBKDF2哈希字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <param name="iterationCount">迭代次数</param>
    /// <param name="derivedKeyLength">派生密钥长度</param>
    /// <returns>PBKDF2哈希字符串</returns>
    public static string PBKDF2(this byte[] @this, int iterationCount = 1000, int derivedKeyLength = 32) =>
        ReadOnlySpanEx.PBKDF2(@this, iterationCount, derivedKeyLength);

    /// <summary>
    /// 验证PBKDF2哈希字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <param name="hash">PBKDF2哈希字符串</param>
    /// <param name="iterationCount">迭代次数</param>
    /// <returns>验证结果</returns>
    public static bool PBKDF2Verify(this byte[] @this, string hash, int iterationCount = 1000) =>
        ReadOnlySpanEx.PBKDF2Verify(@this, hash, iterationCount);

    /// <summary>
    /// 使用AES算法加密字节数组
    /// </summary>
    /// <param name="this">要加密的明文字节数组</param>
    /// <param name="key">加密密钥</param>
    /// <param name="iv">初始化向量</param>
    /// <returns>加密后的字节数组</returns>
    public static byte[] AesEncrypt(this byte[] @this, string key = "", string iv = "")
    {
        ArgumentNullException.ThrowIfNull(@this, nameof(@this));
        return ReadOnlySpanEx.AesEncrypt(@this, key, iv);
    }

    /// <summary>
    /// 使用AES算法解密字节数组
    /// </summary>
    /// <param name="this">要解密的字节数组</param>
    /// <param name="key">解密密钥</param>
    /// <param name="iv">初始化向量</param>
    /// <returns>解密后的字节数组</returns>
    public static byte[] AesDecrypt(this byte[] @this, string key = "", string iv = "")
    {
        ArgumentNullException.ThrowIfNull(@this, nameof(@this));
        return ReadOnlySpanEx.AesDecrypt(@this, key, iv);
    }

    /// <summary>
    /// 使用DES算法加密字节数组
    /// </summary>
    /// <param name="this">要加密的明文字节数组</param>
    /// <param name="key">加密密钥</param>
    /// <param name="iv">初始化向量</param>
    /// <returns>加密后的字节数组</returns>
    public static byte[] DesEncrypt(this byte[] @this, string key = "", string iv = "")
    {
        ArgumentNullException.ThrowIfNull(@this, nameof(@this));
        return ReadOnlySpanEx.DesEncrypt(@this, key, iv);
    }

    /// <summary>
    /// 使用DES算法解密字节数组
    /// </summary>
    /// <param name="this">要解密的字节数组</param>
    /// <param name="key">解密密钥</param>
    /// <param name="iv">初始化向量</param>
    /// <returns>解密后的字节数组</returns>
    public static byte[] DesDecrypt(this byte[] @this, string key = "", string iv = "")
    {
        ArgumentNullException.ThrowIfNull(@this, nameof(@this));
        return ReadOnlySpanEx.DesDecrypt(@this, key, iv);
    }

    /// <summary>
    /// 将字节数组转换为内存流
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <returns>内存流</returns>
    public static MemoryStream ToMemoryStream(this byte[] @this)
    {
        return new MemoryStream(@this);
    }

    /// <summary>
    /// 调整字节数组的大小
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <param name="newSize">新的大小</param>
    /// <returns>调整后的字节数组</returns>
    public static byte[] Resize(this byte[] @this, int newSize)
    {
        Array.Resize(ref @this, newSize);
        return @this;
    }

    /// <summary>
    /// 追加字节数组
    /// </summary>
    /// <param name="this">原始字节数组</param>
    /// <param name="append">要追加的字节数组</param>
    /// <returns>合并后的字节数组</returns>
    public static byte[] Append(this byte[] @this, byte[] append)
    {
        if (append == null || append.Length == 0)
        {
            return @this;
        }

        if (@this == null || @this.Length == 0)
        {
            return append;
        }

        var newBytes = new byte[@this.Length + append.Length];
        @this.CopyTo(newBytes, 0);
        append.CopyTo(newBytes, @this.Length);
        return newBytes;
    }

    /// <summary>
    /// 使用GZip算法压缩字节数组
    /// </summary>
    /// <param name="this">要压缩的字节数组</param>
    /// <returns>压缩后的字节数组</returns>
    public static byte[] GZipCompress(this byte[] @this)
    {
        ArgumentNullException.ThrowIfNull(@this, nameof(@this));
        if (@this.Length == 0) return @this;
        return ReadOnlySpanEx.GZipCompress(@this);
    }

    /// <summary>
    /// 使用GZip算法解压缩字节数组
    /// </summary>
    /// <param name="this">要解压缩的字节数组</param>
    /// <returns>解压缩后的字节数组</returns>
    public static byte[] GZipDecompress(this byte[] @this)
    {
        ArgumentNullException.ThrowIfNull(@this, nameof(@this));
        if (@this.Length == 0) return @this;
        return ReadOnlySpanEx.GZipDecompress(@this);
    }

    /// <summary>
    /// 使用Deflate算法压缩字节数组
    /// </summary>
    /// <param name="this">要压缩的字节数组</param>
    /// <returns>压缩后的字节数组</returns>
    public static byte[] ZipCompress(this byte[] @this)
    {
        ArgumentNullException.ThrowIfNull(@this, nameof(@this));
        if (@this.Length == 0) return @this;
        return ReadOnlySpanEx.ZipCompress(@this);
    }

    /// <summary>
    /// 使用Deflate算法解压缩字节数组
    /// </summary>
    /// <param name="this">要解压缩的字节数组</param>
    /// <returns>解压缩后的字节数组</returns>
    public static byte[] ZipDecompress(this byte[] @this)
    {
        ArgumentNullException.ThrowIfNull(@this, nameof(@this));
        if (@this.Length == 0) return @this;
        return ReadOnlySpanEx.ZipDecompress(@this);
    }
}
